package com.linkhub.oms.admin.modules.user.accountflow.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.date.LocalDateTimeUtil;
import cn.hutool.core.util.IdUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.google.common.collect.Lists;
import com.linkhub.oms.admin.modules.user.account.domain.dto.UserAccountDTO;
import com.linkhub.oms.admin.modules.user.account.service.IUserAccountService;
import com.linkhub.oms.admin.modules.user.accountflow.domain.dto.UserAccountFlowDTO;
import com.linkhub.oms.admin.modules.user.accountflow.domain.param.UserAccountFlowQueryParam;
import com.linkhub.oms.admin.modules.user.accountflow.domain.vo.UserAccountFlowSimpleVO;
import com.linkhub.oms.admin.modules.user.accountflow.domain.vo.UserAccountFlowVO;
import com.linkhub.oms.admin.modules.user.accountflow.service.IUserAccountFlowBizService;
import com.linkhub.oms.admin.modules.user.domain.param.BasePageParam;
import com.linkhub.oms.common.enums.mp.DataStatusEnum;
import com.linkhub.oms.common.enums.mp.DocumentTypeEnum;
import com.linkhub.oms.common.enums.mp.ExpenseTypeEnum;
import com.linkhub.oms.common.enums.mp.FreezeStatusEnum;
import com.linkhub.oms.common.enums.mp.TransactionTypeEnum;
import com.linkhub.oms.common.response.PageDTO;
import com.linkhub.oms.common.util.QtBeanUtil;
import com.linkhub.oms.common.util.QtStreamUtil;
import com.linkhub.oms.dao.mapper.UserAccountFlowEntityMapper;
import com.linkhub.oms.dao.mpservice.IUserService;
import com.linkhub.oms.entity.UserAccountFlowEntity;
import com.linkhub.oms.entity.UserEntity;
import org.apache.commons.lang3.StringUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import javax.annotation.Resource;
import java.math.BigDecimal;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Objects;

/**
 * <p>
 * 用户资金流水 服务实现类
 * </p>
 *
 * @author Frank
 * @since 2024-05-26
 */
@Service
public class UserAccountFlowBizServiceImpl extends ServiceImpl<UserAccountFlowEntityMapper, UserAccountFlowEntity> implements IUserAccountFlowBizService {
    @Resource
    private UserAccountFlowEntityMapper userAccountFlowEntityMapper;
    @Resource
    private IUserService userService;
    @Resource
    private IUserAccountService userAccountService;

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void saveAccountFlow(UserAccountFlowDTO userAccountFlowDTO) {
        UserAccountFlowEntity userAccountFlowEntity = new UserAccountFlowEntity();
        QtBeanUtil.copyProperties(userAccountFlowDTO, userAccountFlowEntity);
        this.save(userAccountFlowEntity);
    }

    @Override
    public PageDTO<UserAccountFlowVO> getAccountFlowPage(UserAccountFlowQueryParam param) {
        LambdaQueryWrapper<UserAccountFlowEntity> queryWrapper = Wrappers.lambdaQuery(UserAccountFlowEntity.class)
                .eq(param.isCustomRoleFlag(), UserAccountFlowEntity::getUserId, param.getUserId())
                .eq(StringUtils.isNoneBlank(param.getDocumentId()), UserAccountFlowEntity::getDocumentId, param.getDocumentId())
                .in(CollUtil.isNotEmpty(param.getMerchantUserIds()), UserAccountFlowEntity::getUserId, param.getMerchantUserIds())
                .in(CollUtil.isNotEmpty(param.getTransactionTypes()), UserAccountFlowEntity::getTransactionType, param.getTransactionTypes())
                //仅查询支出和收入
                .in(CollUtil.isEmpty(param.getTransactionTypes()), UserAccountFlowEntity::getTransactionType,
                        Lists.newArrayList(TransactionTypeEnum.PAY, TransactionTypeEnum.INCOME))
                .in(CollUtil.isNotEmpty(param.getExpenseTypes()), UserAccountFlowEntity::getExpenseType, param.getExpenseTypes())

                .eq(UserAccountFlowEntity::getDataStatus, DataStatusEnum.ENABLE)
                .orderByDesc(UserAccountFlowEntity::getCreatedTime);
        appendQueryDate(queryWrapper, param);

        Page<UserAccountFlowEntity> objectPage = new Page<>(param.getPage(), param.getSize());
        Page<UserAccountFlowEntity> page = super.page(objectPage, queryWrapper);

        PageDTO<UserAccountFlowVO> userRechargeRecordPage = userAccountFlowBuilder(page);
        setUserName(userRechargeRecordPage, param.isCustomRoleFlag());
        return userRechargeRecordPage;
    }

    @Override
    public PageDTO<UserAccountFlowSimpleVO> getFreezeFlowPage(BasePageParam param) {
        LambdaQueryWrapper<UserAccountFlowEntity> queryWrapper = Wrappers.lambdaQuery(UserAccountFlowEntity.class)
                .eq(UserAccountFlowEntity::getUserId, param.getUserId())
                //冻结状态
                .eq(UserAccountFlowEntity::getTransactionType, TransactionTypeEnum.FREEZE)
                //未解冻
                .eq(UserAccountFlowEntity::getUnfreezeStatus, FreezeStatusEnum.FREEZE)
                .eq(UserAccountFlowEntity::getDataStatus, DataStatusEnum.ENABLE)
                .orderByDesc(UserAccountFlowEntity::getCreatedTime);
        Page<UserAccountFlowEntity> objectPage = new Page<>(param.getPage(), param.getSize());
        Page<UserAccountFlowEntity> page = super.page(objectPage, queryWrapper);
        return userAccountFlowSimpleBuilder(page);
    }

    @Override
    public List<UserAccountFlowEntity> queryAccountFlow(String documentId,
                                                        TransactionTypeEnum transactionType,
                                                        DocumentTypeEnum documentType) {

        return super.lambdaQuery()
                .eq(UserAccountFlowEntity::getDocumentId, documentId)
                .eq(UserAccountFlowEntity::getTransactionType, transactionType)
                .eq(UserAccountFlowEntity::getDocumentType, documentType)
                .list();


    }

    /**
     * 设置查询时间
     */
    private void appendQueryDate(LambdaQueryWrapper<UserAccountFlowEntity> queryWrapper, UserAccountFlowQueryParam param) {
        LocalDateTime dateBegin;
        LocalDateTime dateEnd;
        LocalDateTime now = LocalDateTime.now();
        if (StringUtils.isBlank(param.getTransactionDateBegin()) || StringUtils.isBlank(param.getTransactionDateEnd())) {
            //默认查询6个月
            dateBegin = LocalDateTimeUtil.beginOfDay(now).minusDays(180);
            dateEnd = LocalDateTimeUtil.endOfDay(now);
        } else {
            dateBegin = LocalDateTimeUtil.beginOfDay(LocalDateTimeUtil.of(LocalDate.parse(param.getTransactionDateBegin())));
            dateEnd = LocalDateTimeUtil.endOfDay(LocalDateTimeUtil.of(LocalDate.parse(param.getTransactionDateEnd())));
        }
        queryWrapper.le(UserAccountFlowEntity::getCreatedTime, dateEnd)
                .ge(UserAccountFlowEntity::getCreatedTime, dateBegin);
    }

    /**
     * 构建PageDTO<UserRechargeRecordVO>
     */
    private PageDTO<UserAccountFlowVO> userAccountFlowBuilder(Page<UserAccountFlowEntity> page) {
        PageDTO<UserAccountFlowVO> res = new PageDTO<>();
        res.setTotal(page.getTotal());
        res.setSize(page.getSize());
        res.setCurrent(page.getCurrent());
        res.setPages(page.getPages());
        List<UserAccountFlowVO> userAccountFlows = QtStreamUtil.map(page.getRecords(), UserAccountFlowVO::new);
        res.setRecords(userAccountFlows);
        return res;
    }

    /**
     * 设置商户名称
     */
    private void setUserName(PageDTO<UserAccountFlowVO> userAccountFlowPage, boolean customRoleFlag) {
        List<UserAccountFlowVO> records = userAccountFlowPage.getRecords();
        if (!customRoleFlag && CollUtil.isNotEmpty(records)) {
            List<String> userIds = QtStreamUtil.mapAndDistinct(records, UserAccountFlowVO::getMerchantUserId);
            List<UserEntity> userEntities = userService.listByUserId(userIds);
            Map<String, UserEntity> userIdUserMap = QtStreamUtil.toMap(userEntities, UserEntity::getUserId);
            records.forEach(record -> {
                UserEntity userEntity = userIdUserMap.get(record.getMerchantUserId());
                if (Objects.nonNull(userEntity)) {
                    record.setMerchantUserName(userEntity.getUserName());
                }
            });
        }
    }

    /**
     * 构建PageDTO<UserRechargeRecordVO>
     */
    private PageDTO<UserAccountFlowSimpleVO> userAccountFlowSimpleBuilder(Page<UserAccountFlowEntity> page) {
        PageDTO<UserAccountFlowSimpleVO> res = new PageDTO<>();
        res.setTotal(page.getTotal());
        res.setSize(page.getSize());
        res.setCurrent(page.getCurrent());
        res.setPages(page.getPages());
        List<UserAccountFlowSimpleVO> userAccountFlowSimples = QtStreamUtil.map(page.getRecords(), UserAccountFlowSimpleVO::new);
        res.setRecords(userAccountFlowSimples);
        return res;
    }

    /**
     * 解冻单据
     * @param documentId 单据id
     * @param documentTypeEnum 单据类型
     * @param needPay 解冻后是否需要支付
     * @param realPayMap 最终真实要支付的金额
     */
    private void unfreezeAccountFlowByDocumentId(String documentId, DocumentTypeEnum documentTypeEnum, boolean needPay, Map<ExpenseTypeEnum, BigDecimal> realPayMap) {
        List<UserAccountFlowEntity> accountFlows = this.queryAccountFlow(documentId,TransactionTypeEnum.FREEZE, documentTypeEnum);

        if (CollUtil.isEmpty(accountFlows)) {
            return;
        }

        // accountFlows 更新是否解冻
        List<UserAccountFlowEntity> updateUnfreezeStatusList = QtStreamUtil.map(accountFlows, flow -> {
            UserAccountFlowEntity entity = new UserAccountFlowEntity();
            entity.setId(flow.getId());
            entity.setUnfreezeStatus(FreezeStatusEnum.UN_FREEZE);
            return entity;
        });
        this.updateBatchById(updateUnfreezeStatusList);

        // 更新冻结金额
        String userId = accountFlows.get(0).getUserId();
        BigDecimal freezeBalance = QtStreamUtil.sum(accountFlows, UserAccountFlowEntity::getAmount);
        UserAccountDTO accountDTO = userAccountService.updateAccountAmount(userId, freezeBalance, TransactionTypeEnum.UN_FREEZE);

        List<UserAccountFlowEntity> newAccountFlows = Lists.newArrayList();
        List<UserAccountFlowEntity> unfreezeFlows = QtStreamUtil.map(accountFlows, flow -> {
            UserAccountFlowEntity entity = new UserAccountFlowEntity();
            entity.setUserId(flow.getUserId());
            entity.setTransactionId(IdUtil.getSnowflake().nextIdStr());
            entity.setTransactionType(TransactionTypeEnum.UN_FREEZE);
            entity.setDocumentId(flow.getDocumentId());
            entity.setDocumentType(documentTypeEnum);
            entity.setExpenseType(flow.getExpenseType());
            entity.setAmount(flow.getAmount());
            entity.setRemainingBalance(accountDTO.getTotalBalance());

            return entity;
        });
       newAccountFlows.addAll(unfreezeFlows);

        if(needPay){
            //解冻后需要支付
            BigDecimal payBalance = freezeBalance;
            if(CollUtil.isNotEmpty(realPayMap)){
                accountFlows.forEach(flow -> flow.setAmount(realPayMap.getOrDefault(flow.getExpenseType(),flow.getAmount())));
                payBalance = QtStreamUtil.sum(accountFlows, UserAccountFlowEntity::getAmount);
            }

            UserAccountDTO accountDTO1 = userAccountService.updateAccountAmount(userId, payBalance, TransactionTypeEnum.PAY);
            List<UserAccountFlowEntity> payFlows = QtStreamUtil.map(accountFlows, flow -> {
                UserAccountFlowEntity entity = new UserAccountFlowEntity();
                entity.setUserId(flow.getUserId());
                entity.setTransactionId(IdUtil.getSnowflake().nextIdStr());
                entity.setTransactionType(TransactionTypeEnum.PAY);
                entity.setDocumentId(flow.getDocumentId());
                entity.setDocumentType(documentTypeEnum);
                entity.setExpenseType(flow.getExpenseType());
                entity.setAmount(flow.getAmount());
                entity.setRemainingBalance(accountDTO1.getTotalBalance());

                return entity;
            });

            newAccountFlows.addAll(payFlows);
        }

        this.saveBatch(newAccountFlows);

    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unfreezeAccountFlowByDocumentId(String documentId, DocumentTypeEnum documentTypeEnum) {
        unfreezeAccountFlowByDocumentId(documentId,documentTypeEnum,false, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unfreezeAccountFlowAndPayByDocumentId(String documentId, DocumentTypeEnum documentTypeEnum){
        unfreezeAccountFlowByDocumentId(documentId,documentTypeEnum,true, null);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void unfreezeAccountFlowAndPayByDocumentId(String documentId, DocumentTypeEnum documentTypeEnum,Map<ExpenseTypeEnum, BigDecimal> realPayMap){
        unfreezeAccountFlowByDocumentId(documentId,documentTypeEnum,true, realPayMap);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void refundOrderAccountFlowByDocumentId(String documentId) {
        List<UserAccountFlowEntity> accountFlows = this.queryAccountFlow(documentId,TransactionTypeEnum.PAY, DocumentTypeEnum.ORDER);

        if (CollUtil.isEmpty(accountFlows)) {
            return;
        }
        String userId = accountFlows.get(0).getUserId();

        // 操作费不退
        accountFlows = QtStreamUtil.filter(accountFlows,f -> !ExpenseTypeEnum.OPERATE.equals(f.getExpenseType()));

        BigDecimal refundBalance = QtStreamUtil.sum(accountFlows, UserAccountFlowEntity::getAmount);
        UserAccountDTO accountDTO = userAccountService.updateAccountAmount(userId, refundBalance, TransactionTypeEnum.INCOME);

        List<UserAccountFlowEntity> refundFlows = QtStreamUtil.map(accountFlows, flow -> {
            UserAccountFlowEntity entity = new UserAccountFlowEntity();
            entity.setUserId(flow.getUserId());
            entity.setTransactionId(IdUtil.getSnowflake().nextIdStr());
            entity.setTransactionType(TransactionTypeEnum.INCOME);
            entity.setDocumentId(flow.getDocumentId());
            entity.setDocumentType(DocumentTypeEnum.RETURN_ORDER);
            entity.setExpenseType(flow.getExpenseType());
            entity.setAmount(flow.getAmount());
            entity.setRemainingBalance(accountDTO.getTotalBalance());

            return entity;
        });
        this.saveBatch(refundFlows);
    }

}
